Skip to content

[pull] main from fern-api:main#728

Merged
pull[bot] merged 16 commits into
code:mainfrom
fern-api:main
May 14, 2026
Merged

[pull] main from fern-api:main#728
pull[bot] merged 16 commits into
code:mainfrom
fern-api:main

Conversation

@pull
Copy link
Copy Markdown

@pull pull Bot commented May 14, 2026

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

fern-support and others added 16 commits May 14, 2026 12:00
Co-authored-by: davidkonigsberg <72822263+davidkonigsberg@users.noreply.github.com>
* Add description to schemas

* Add changelog entry

* Update snapshots

* Update json schemas

* Update snapshots
…) (#15869)

* chore(python): bump Poetry to 2.4.1 (CVE-2026-34591)

Poetry <2.3.3 stored credentials in cleartext when the keyring backend was unavailable. Bumping past 2.3.3 (to the current latest 2.4.1) clears the finding in the python-sdk + pydantic-model container scans.

The python-sdk Dockerfile uses 'virtualenvs.create=false', which installs deps into the same env as Poetry itself. Poetry 2.4.1 ships poetry-core 2.4.0, so pyproject.toml's poetry-core constraint moves from ^1.9.0 to ^2.0.0 to stay in lockstep and avoid the resolver swapping poetry-core under Poetry's feet. poetry.lock regenerated under Poetry 2.4.1.

* chore(python): shorten comments per PR review

---------

Co-authored-by: Devin AI <devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: davidkonigsberg <72822263+davidkonigsberg@users.noreply.github.com>
…lative (#15817)

* fix(cli-v2): adjust file paths in fern config migrate to be root-relative

Paths from generators.yml (relative to fern/) were not being adjusted
when written to the root fern.yml. This caused 'file does not exist'
errors for openapi specs, overrides, and docs.yml references.

- Add adjustSpecPathsForSingleApi to re-root single-API spec paths
- Fix docs  to use ./fern/docs.yml instead of ./docs.yml
- Normalize paths with .. segments (e.g. ../external/ -> ./external/)
- Add test for single-API path adjustment
- Update existing test assertions

Resolves FER-10462

* refactor: compute migration paths dynamically using path.relative instead of hardcoding FERN_DIRECTORY

* fix: resolve biome lint errors (import ordering and unused import)

* Handle empty sourcePrefix in rebasePath

Add handling for when sourcePrefix is an empty string so rebased spec paths are returned as "./<file>" rather than producing an extra slash or malformed path. Also add a unit test ensuring spec paths remain correct when fernDir equals projectRoot (empty sourcePrefix). This prevents incorrect './ /file' or double-slash paths for specs and overrides.

---------

Co-authored-by: Naman Anand <info@buildwithfern.com>
…5813)

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: dsinghvi <10870189+dsinghvi@users.noreply.github.com>
Co-authored-by: Swimburger <3382717+Swimburger@users.noreply.github.com>
…dation (#15818)

* feat(cli): add fern docs check --links command for live site link validation

Co-Authored-By: Deep Singhvi <deep@buildwithfern.com>

* refactor: restructure to fern docs link check --url

Move link checker from 'fern docs check --links --instance' to
'fern docs link check --url' for clearer UX. Removes fern-specific
jargon ('instance') in favor of user-friendly '--url' flag.

Co-Authored-By: Deep Singhvi <deep@buildwithfern.com>

* fix: restructure broken/blocked link check logic per review

Co-Authored-By: Deep Singhvi <deep@buildwithfern.com>

* feat: wire up fern docs link check in main CLI entry point

The main CLI (packages/cli/cli) registers docs subcommands directly
in cli.ts, not through cli-v2. Added addDocsLinkCommand and
addDocsLinkCheckCommand to the main CLI, importing LinkCheckClient,
LinkCheckFormatter, and ProgressRenderer from @fern-api/cli-v2.

Co-Authored-By: Deep Singhvi <deep@buildwithfern.com>

* fix: address Claude review — SSE error handling and progress cleanup

- SSE 'error' events now capture the message and throw LinkCheckError
  after the stream closes, preventing false 'All links valid' output
- Move progress.finish() to finally block so TTY progress line is
  always cleared, even on mid-stream errors
- Fix biome import ordering in cli-v2/src/index.ts and cli/src/cli.ts

Co-Authored-By: Deep Singhvi <deep@buildwithfern.com>

* fix: resolve TypeScript errors in main CLI link check command

- Use token.value (FernToken has a .value property, not string)
- Access config.instances instead of nonexistent docsInstances
- Rename map parameter to avoid implicit any

Co-Authored-By: Deep Singhvi <deep@buildwithfern.com>

* fix: align SSE client with actual server event schema

- sitemap_fetched: totalPages (not pageCount)
- page_scraped: pageUrl/pageIndex (not url/pagesScraped)
- link_checked: statusCode/isInternal (not status/type/classification)
- Add link_check_progress handler for progress bar during link checking
- Add links_check_started handler for total link count
- Use complete event's brokenLinks/blockedLinks arrays as source of truth
- Update formatter to use new BrokenLink field names

Co-Authored-By: Deep Singhvi <deep@buildwithfern.com>

* feat: improve link check output formatting with colored output, progress bars, and local file resolution

Co-Authored-By: Deep Singhvi <deep@buildwithfern.com>

* fix: biome import ordering for SourceResolver

Co-Authored-By: Deep Singhvi <deep@buildwithfern.com>

* fix: address all claude review comments on link checker

- Replace blind `as` type assertions with type guard functions for all SSE events
- Add `default: assertNever()` to output format switch statement
- Add explanatory comment to empty catch block for non-JSON SSE lines
- Remove double error printing (onError callback removed, error thrown after stream)
- Add `sawComplete` guard to throw if stream ends without completion event
- Fix SourceResolver: use path.dirname() since docsAbsolutePath is a file path
- Fix SourceResolver: boundary-aware link matching to prevent false positives
- Add `error` field to blocked links JSON output
- Fix output flushing: await drain before throwing CliError in v1 CLI
- Add pathname-without-slash search pattern for relative link matching

Co-Authored-By: Deep Singhvi <deep@buildwithfern.com>

* fix: address remaining claude review comments

- Add prefix boundary check to findLinkMatch (prevents /api matching /foo/api)
- Remove overly permissive pathname.slice(1) pattern from getSearchPatterns
- Map 404 → ConfigError in v1 CLI to match v2 behavior
- Add comments to empty catch blocks in walkDir and getDocsConfigPath

Co-Authored-By: Deep Singhvi <deep@buildwithfern.com>

* fix(cli): align progress bar indent, fix output flushing race, deduplicate project load

Co-Authored-By: Deep Singhvi <deep@buildwithfern.com>

* refactor: replace [broken-link]/[blocked-link] with [docs]: prefix, remove footer and SourceResolver heuristic

- Change link prefixes from [broken-link]/[blocked-link] to [docs]: for consistency
- Remove 'Broken links found' redundant footer line
- Simplify SourceResolver to pass through source page URLs directly
  (file:line:column resolution will be handled server-side via pageId)
- Remove unused resolveDocsConfigPath function and docsAbsolutePath plumbing

Co-Authored-By: Deep Singhvi <deep@buildwithfern.com>

* fix: prevent duplicate error message for unauthorized link check

Co-Authored-By: Deep Singhvi <deep@buildwithfern.com>

* fix formatting and personal taste for UX

* fix: address review comments — CSV escape, dead code, stderr routing, type guards, changelog

Co-Authored-By: Deep Singhvi <deep@buildwithfern.com>

* feat: consume pageId from SSE and resolve to local file:line:column

Co-Authored-By: Deep Singhvi <deep@buildwithfern.com>

* fix: resolve docsConfigDir even when --url is provided for sourcePageId resolution

Co-Authored-By: Deep Singhvi <deep@buildwithfern.com>

* fix: resolve docsConfigDir in v1 CLI when --url is provided

Co-Authored-By: Deep Singhvi <deep@buildwithfern.com>

* fix: allow runtime override of dashboard URL via FERN_DASHBOARD_URL_OVERRIDE

process.env.FERN_DASHBOARD_URL is replaced at build time by tsup's env
option, making it impossible to override at runtime for testing preview
deployments. Add FERN_DASHBOARD_URL_OVERRIDE which uses bracket notation
to avoid compile-time replacement, enabling runtime override for both
v1 and v2 CLI paths.

Co-Authored-By: Deep Singhvi <deep@buildwithfern.com>

* fix: format DASHBOARD_BASE_URL for biome line length

Co-Authored-By: Deep Singhvi <deep@buildwithfern.com>

* refactor: make FERN_DASHBOARD_URL a clean runtime override

Rename build-time env var to FERN_DASHBOARD_URL_DEFAULT so that
FERN_DASHBOARD_URL can be set at runtime to point at any arbitrary
dashboard URL (Vercel preview, localhost, etc.).

- All 4 build scripts now bake FERN_DASHBOARD_URL_DEFAULT
- Runtime resolution: FERN_DASHBOARD_URL > FERN_DASHBOARD_URL_DEFAULT > hardcoded fallback
- Single shared constant exported from @fern-api/login
- v1 and v2 CLI both import from @fern-api/login instead of duplicating

Co-Authored-By: Deep Singhvi <deep@buildwithfern.com>

* fix: biome import ordering for DASHBOARD_BASE_URL

Co-Authored-By: Deep Singhvi <deep@buildwithfern.com>

* fix: filter out auto-generated API reference pageIds from link check output

Only show source files that exist locally. Auto-generated API reference
pages (e.g. tag-plant.md) are omitted since they don't correspond to
user-authored files. Falls back to source page URLs when no local files
resolve.

Co-Authored-By: Deep Singhvi <deep@buildwithfern.com>

* refactor: remove line:column resolution from link checker

Simplify SourceResolver to only show file paths without line:column
numbers. Line:column support can be added back as a follow-up with
better URL matching (basepath stripping, relative paths, etc.).

Co-Authored-By: Deep Singhvi <deep@buildwithfern.com>

* fix: improve error message when link check stream is interrupted

Instead of the unhelpful 'stream ended unexpectedly without a completion
event', the CLI now:
- Shows which phase was interrupted (connecting/scraping/checking)
- Includes progress stats (pages scraped, links checked)
- Displays partial results if any broken links were found
- Only throws an error if no results were collected at all

Co-Authored-By: Deep Singhvi <deep@buildwithfern.com>

* fix: filter auto-generated API reference pages even without local workspace

PageIds without a directory separator (e.g. 'tag-plant.md') are
auto-generated API reference entries. These are now filtered out
regardless of whether a local workspace is available.

Co-Authored-By: Deep Singhvi <deep@buildwithfern.com>

* fix: show URL slug for auto-generated API reference pages

Instead of hiding auto-generated API reference pages entirely, show
the URL slug (e.g. /platform/api-reference/api-reference/plant) as
the source reference. User-authored pages still show local file paths.

Co-Authored-By: Deep Singhvi <deep@buildwithfern.com>

* chore: update pnpm-lock.yaml after rebase

Co-Authored-By: Deep Singhvi <deep@buildwithfern.com>

* fix: rename JSON fields (display→slug, localFile→filePath), fix partial-result counters and basepath stripping

Co-Authored-By: Deep Singhvi <deep@buildwithfern.com>

* fix: format long ternary line for biome

Co-Authored-By: Deep Singhvi <deep@buildwithfern.com>

* fix: address all Claude Code review comments on link check command

- LinkCheckClient: flush SSE buffer/decoder after read loop exits
- LinkCheckClient: extract processLine helper to avoid duplication
- LinkCheckClient: move onStreamInterrupted inside partial-result branch
- LinkCheckClient: use actual counts in partial-result return (pagesScrapedSoFar, linksCheckedSoFar, computed workingLinks)
- LinkCheckClient: pass pageIndex+1 (1-based count) to onPageScraped callbacks
- ProgressRenderer: rename parameter to pagesScraped for clarity
- LinkCheckFormatter: add statusCode to blocked link text output
- LinkCheckFormatter: add link.error display in text output for broken and blocked
- LinkCheckFormatter: rename CSV header isInternal -> scope to match values
- LinkCheckFormatter: fix stripBasePath regression by using toRelativePath for URL slugs
- LinkCheckFormatter: thread interrupted flag for 'Interrupted after' vs 'Finished in'
- command.ts (v2): use process.stdout.write for JSON/CSV to bypass logger prefix
- cli.ts (v1): add onStreamInterrupted callback matching v2
- cli.ts (v1): add 'Link check failed:' prefix for InternalError
- cli.ts (v1): route error messages to stderr before failAndThrow in resolveDocsLinkCheckContext
- SourceResolver: gate sourcePageIds branch on docsConfigDir != null
- Changelog: fix inaccurate descriptions (removed [docs]: prefix claim, line:column claim)

Co-Authored-By: ryanstep <ryanstep@umich.edu>

* fix: address round-2 Claude Code comments

- ProgressRenderer: add non-TTY completion message for link checking phase
- LinkCheckFormatter: add 'status' field to JSON summary (complete/interrupted)
- LinkCheckFormatter: add '# status:' comment to CSV output header

Co-Authored-By: ryanstep <ryanstep@umich.edu>

* fix: use totalLinks for phase detection to cover links_check_started window

Co-Authored-By: ryanstep <ryanstep@umich.edu>

* fix: address round-3 Claude Code review comments

- command.ts: add explanatory comment to empty catch block
- command.ts: make normalizeDomain case-insensitive for scheme prefix
- cli.ts: make normalizeDomain case-insensitive for scheme prefix
- LinkCheckClient: normalize trailing slash on dashboardUrl
- LinkCheckFormatter: replace CSV comment line with RFC 4180 runStatus column
- cli.ts: add error handler to writeAndDrain to prevent hangs on EPIPE

Co-Authored-By: ryanstep <ryanstep@umich.edu>

* fix: address remaining Claude Code review comments (round 4)

- LinkCheckClient: add null/type check after JSON.parse, comment empty catch,
  remove unused SseEvent interface, use ParsedSseEvent with validation
- LinkCheckFormatter: use toRelativePath for internal blocked links,
  add segment boundary check to stripBasePath, fix escapeCsv for CR,
  stop double-calling toRelativePath in formatReference/formatJsonReference
- SourceResolver: add comment to empty catch, fix isUserAuthoredPage
  to recognize root .mdx/.md files

Co-Authored-By: ryanstep <ryanstep@umich.edu>

* fix: deeper SSE validation, type-safe complete data, progress ordering

- LinkCheckClient: validate data field exists and is object in SSE parser,
  add toBrokenLink helper for type-safe conversion from validated data,
  validate complete event array elements with isLinkCheckedData,
  narrow isCompleteData return type to match actual validation (unknown[])
- LinkCheckFormatter: use ref.slug directly in CSV (no double processing)
- cli.ts: call progress.finish() before error message in catch block
  to prevent garbled TTY output from active progress bar

Co-Authored-By: ryanstep <ryanstep@umich.edu>

* fix: defer DASHBOARD_BASE_URL to invocation time, bypass logger for text output

- Convert DASHBOARD_BASE_URL const to getDashboardBaseUrl() getter so
  .env file overrides are respected after dotenv loading
- Update all call sites (cli.ts, command.ts, login.ts, index.ts)
- Use process.stderr.write for text output in v2 command to bypass
  logger prefix corruption with --log-level debug

Co-Authored-By: ryanstep <ryanstep@umich.edu>

* fix: handle SSE data prefix without space, guard null array elements

- Support both 'data: {...}' and 'data:{...}' SSE prefix forms per
  WHATWG SSE spec (space after colon is optional)
- Add null/object guard before casting complete event array elements
  to prevent TypeError on malformed payloads

Co-Authored-By: ryanstep <ryanstep@umich.edu>

* fix: revert isUserAuthoredPage extension check, use warn for stream interruption

- Revert isUserAuthoredPage to only check for '/' (existsSync handles
  root-level files; .md/.mdx extension check misclassifies auto-generated
  pages like tag-plant.md)
- Use stderr.warn instead of stderr.info for onStreamInterrupted in both
  v1 and v2 so the data-fidelity warning is visible with --log-level warn

Co-Authored-By: ryanstep <ryanstep@umich.edu>

* fix: remove unreachable \r\n check in escapeCsv

The \r\n check is unreachable since \r is already checked on the
preceding line. Any string with \r\n necessarily contains \r.

Co-Authored-By: ryanstep <ryanstep@umich.edu>

* fix: replace blind as cast on error response body with runtime validation

Validate response.json() shape with typeof checks before accessing
message/error fields, matching the type-guard pattern used for SSE
event data throughout the file.

Co-Authored-By: ryanstep <ryanstep@umich.edu>

* quick fix for formatting

* fix: mirror v2 status message formatting in v1 CLI

Add Icons and chalk formatting to v1 'Checking links' and 'All links
valid' messages to match v2 styling (cyan domain, green checkmark).

Co-Authored-By: ryanstep <ryanstep@umich.edu>

* fix: sort imports in cli.ts

Co-Authored-By: ryanstep <ryanstep@umich.edu>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Ryan Amirthan Stephen <105958906+Ryan-Amirthan@users.noreply.github.com>
Co-authored-by: ryanstep <ryanstep@umich.edu>
@pull pull Bot locked and limited conversation to collaborators May 14, 2026
@pull pull Bot added the ⤵️ pull label May 14, 2026
@pull pull Bot merged commit 05b9c6a into code:main May 14, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants